/*******************************************************************************
 * Copyright (c) 2010 Michael Doberenz. All rights reserved. This program and
 * the accompanying materials are made available under the terms of the Eclipse
 * Public License v1.0 which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/epl-v10.html Contributors: Michael Doberenz -
 * initial implementation
 *******************************************************************************/
package org.fuwjin.pogo;

import static org.fuwjin.util.ObjectUtils.eq;
import static org.fuwjin.util.ObjectUtils.hash;

import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Map;

import org.fuwjin.pogo.postage.PogoCategory;
import org.fuwjin.postage.Category;
import org.fuwjin.postage.Postage;
import org.fuwjin.postage.category.VoidCategory;

/**
 * The parser generated by PogoGrammar.
 */
public class Grammar extends Pogo implements Iterable<Rule> {
   private final Map<String, Rule> rules = new LinkedHashMap<String, Rule>();
   private final Postage postage;

   /**
    * Creates a new instance.
    */
   public Grammar() {
      this(new Postage(new PogoCategory(), new VoidCategory("default")));
   }

   /**
    * Creates a new instance.
    * @param postage the custom postage.
    */
   public Grammar(final Postage postage) {
      this.postage = postage;
   }

   /**
    * Adds a new rule to the grammar.
    * @param rule the new rule
    */
   protected void add(final Rule rule) {
      if(rules.isEmpty()) {
         setRule(rule);
      }
      rules.put(rule.name(), rule);
   }

   @Override
   public boolean equals(final Object obj) {
      try {
         final Grammar o = (Grammar)obj;
         return eq(getClass(), o.getClass()) && eq(rules, o.rules);
      } catch(final ClassCastException e) {
         return false;
      }
   }

   /**
    * Retrieves a rule from the grammar by name.
    * @param name the name of the rule
    * @return the rule by that name, or null if there is no such rule
    */
   public Pogo get(final String name) {
      final Rule rule = rules.get(name);
      if(rule == null) {
         throw new IllegalArgumentException(name);
      }
      return new Pogo(rule);
   }

   /**
    * Returns the named category.
    * @param category the name of the category
    * @return the category
    */
   public Category getCategory(final String category) {
      return postage.getCategory(category);
   }

   /**
    * Returns the named rule.
    * @param name the rule name
    * @return the rule
    */
   public Rule getRule(final String name) {
      return rules.get(name);
   }

   @Override
   public int hashCode() {
      return hash(getClass(), rules);
   }

   @Override
   public Iterator<Rule> iterator() {
      return rules.values().iterator();
   }

   /**
    * Resolves the rules in this grammar.
    * @return this grammar
    */
   protected Grammar resolve() {
      for(final Rule rule: rules.values()) {
         rule.resolve(this, null);
      }
      return this;
   }

   /**
    * Returns the generated code corresponding to this grammar.
    * @param qualifiedName the package/classname of the generated class
    * @return the generated code
    * @throws PogoException if there was a serialization error
    */
   public String toCode(final String qualifiedName) throws PogoException {
      return PredefinedGrammar.grammarToJava(qualifiedName, this);
   }

   /**
    * Returns the pogo grammar for this grammar.
    * @return the generated pogo grammar
    * @throws PogoException if there was a serialization error
    */
   public String toPogo() throws PogoException {
      return PredefinedGrammar.grammarToPogo(this);
   }

   @Override
   public String toString() {
      return "Grammar " + super.toString();
   }
}
